package com.yayohos.locationmanager.providers.locationprovider;


import com.yayohos.locationmanager.constants.FailType;
import com.yayohos.locationmanager.constants.ProviderType;
import com.yayohos.locationmanager.constants.RequestCode;
import com.yayohos.locationmanager.helper.LogUtils;
import com.yayohos.locationmanager.helper.continuoustask.ContinuousTask;
import com.yayohos.locationmanager.listener.DialogListener;
import com.yayohos.locationmanager.providers.dialogprovider.DialogProvider;

import ohos.aafwk.content.Intent;
import ohos.agp.window.dialog.BaseDialog;
import ohos.location.Location;
import ohos.location.LocatorCallback;
import ohos.location.RequestParam;
import ohos.utils.IntentConstants;

/**
 * DefaultLocationProvider
 *
 * @date 2021/5/26
 */
public class DefaultLocationProvider extends LocationProvider
        implements ContinuousTask.ContinuousTaskRunner, LocatorCallback, DialogListener {
    private DefaultLocationSource defaultLocationSource;
    private BaseDialog gpsDialog;

    @Override
    public void onDestroy() {
        super.onDestroy();
        gpsDialog = null;
        getSourceProvider().removeSwitchTask();
        getSourceProvider().removeUpdateRequest();
        getSourceProvider().removeLocationUpdates(this);
    }

    @Override
    public void cancel() {
        LogUtils.logI("cancel++++++++++++");
        getSourceProvider().getUpdateRequest().release();
        getSourceProvider().getProviderSwitchTask().stop();
    }

    @Override
    public void onPause() {
        super.onPause();
        LogUtils.logI("onPause++++++++++++");
        getSourceProvider().getUpdateRequest().release();
        getSourceProvider().getProviderSwitchTask().pause();
    }

    @Override
    public void onResume() {
        super.onResume();
        LogUtils.logI("onResume++++++++++++");
        LogUtils.logI("isLocationSwitchOn+++" + isLocationSwitchOn());
        getSourceProvider().getUpdateRequest().run();
        if (isWaiting()) {
            getSourceProvider().getProviderSwitchTask().resume();
        }
        if (isDialogShowing() && isLocationSwitchOn()) {
            gpsDialog.hide();
            onGPSActivated();
        }
    }

    @Override
    public boolean isDialogShowing() {
        return gpsDialog != null && gpsDialog.isShowing();
    }

    @Override
    public void onAbilityResult(int requestCode, int resultCode, Intent data) {
        super.onAbilityResult(requestCode, resultCode, data);
        LogUtils.logI("onAbilityResult==" + requestCode);
        if (requestCode == RequestCode.GPS_ENABLE) {
            if (isLocationSwitchOn()) {
                onGPSActivated();
            } else {
                onLocationFailed(FailType.GPS_NOT_OPEN);
            }
        }
    }

    @Override
    public void get() {
        LogUtils.logI("get++++++++++");
        setWaiting(true);
        if (isLocationSwitchOn()) {
            LogUtils.logI("GPS is already enabled, getting location...");
            askForLocation();
        } else {
            // GPS is not enabled,
            if (getConfiguration().defaultProviderConfiguration().askForEnableGPS() && getAbility() != null) {
                LogUtils.logI("GPS is not enabled, asking user to enable it...");
                askForEnableGPS();
            } else {
                onLocationFailed(FailType.GPS_NOT_OPEN);
            }
        }
    }

    void askForEnableGPS() {
        LogUtils.logI("askForEnableGPS");
        DialogProvider gpsDialogProvider = getConfiguration().defaultProviderConfiguration().gpsDialogProvider();
        gpsDialogProvider.setDialogListener(this);
        gpsDialog = gpsDialogProvider.getDialog(getAbility());
        gpsDialog.show();
    }

    void onGPSActivated() {
        LogUtils.logI("User activated GPS, listen for location");
        askForLocation();
    }

    void askForLocation() {
        LogUtils.logI("Ask for location ...");
        if (!getSourceProvider().getProviderSwitchTask().isIdle()) {
            getSourceProvider().getProviderSwitchTask().stop();
        }
        boolean locationIsAlreadyAvailable = checkForLastKnowLocation();
        if (getConfiguration().keepTracking() || !locationIsAlreadyAvailable) {
            LogUtils.logI("Ask for location update...");
            if (!locationIsAlreadyAvailable) {
                getSourceProvider().getProviderSwitchTask().delayed(getWaitPeriod());
            }
            requestUpdateLocation();
        } else {
            LogUtils.logI("We got location, no need to ask for location updates.");
        }
    }

    boolean checkForLastKnowLocation() {
        Location lastKnownLocation = getSourceProvider().getLastKnownLocation();
        if (getSourceProvider().isLocationSufficient(lastKnownLocation,
                getConfiguration().defaultProviderConfiguration().acceptableTimePeriod(),
                getConfiguration().defaultProviderConfiguration().acceptableAccuracy())) {
            LogUtils.logI("LastKnowLocation is usable.");
            onLocationReceived(lastKnownLocation);
            return true;
        } else {
            LogUtils.logI("LastKnowLocation is not usable.");
        }

        return false;
    }

    void notifyProcessChange() {
        if (getListener() != null) {
            getListener().onProcessTypeChanged(ProviderType.DEFAULT_PROVIDERS);
        }
    }

    void requestUpdateLocation() {
        int timeInterval = (int) getConfiguration().defaultProviderConfiguration().requiredTimeInterval();
        int distanceInterval = (int) getConfiguration().defaultProviderConfiguration().requiredDistanceInterval();
        getSourceProvider().getUpdateRequest().run(RequestParam.PRIORITY_ACCURACY, timeInterval, distanceInterval);
    }


    long getWaitPeriod() {
        return getConfiguration().defaultProviderConfiguration().gpsWaitPeriod();
    }


    private boolean isLocationSwitchOn() {
        return getSourceProvider().isLocationSwitchOn();
    }

    void onLocationReceived(Location location) {
        if (getListener() != null) {
            getListener().onLocationChanged(location);
        }
        setWaiting(false);
    }

    void onLocationFailed(int type) {
        if (getListener() != null) {
            getListener().onLocationFailed(type);
        }
        setWaiting(false);
    }

    @Override
    public void onLocationReport(Location location) {
        if (getSourceProvider().updateRequestIsRemoved()) {
            return;
        }
        onLocationReceived(location);
        // Remove cancelLocationTask because we have already find location,
        // no need to switch or call fail
        if (!getSourceProvider().switchTaskIsRemoved()) {
            getSourceProvider().getProviderSwitchTask().stop();
        }
        if (!getConfiguration().keepTracking()) {
            getSourceProvider().getUpdateRequest().release();
            getSourceProvider().removeLocationUpdates(this);
        }
    }


    @Override
    public void onStatusChanged(int status) {
        if (getListener() != null) {
            getListener().onStatusChanged(status);
        }
    }

    @Override
    public void onErrorReport(int error) {
        if (getListener() != null) {
            getListener().onLocationFailed(error);
        }
    }

    @Override
    public void runScheduledTask(String taskId) {
        if (taskId.equals(DefaultLocationSource.PROVIDER_SWITCH_TASK)) {
            getSourceProvider().getUpdateRequest().release();
            onLocationFailed(FailType.TIMEOUT);
        }
    }


    @Override
    public void onPositiveButtonClick() {
        Intent intent = new Intent();
        Intent.OperationBuilder builder = new Intent.OperationBuilder();
        builder.withAction(IntentConstants.ACTION_MANAGE_APPLICATIONS_SETTINGS);
        intent.setOperation(builder.build());
        intent.addFlags(Intent.FLAG_ABILITY_CONTINUATION);
        getAbility().startAbility(intent);
    }

    @Override
    public void onNegativeButtonClick() {
        LogUtils.logI("User didn't want to enable GPS,calling fail...");
        onLocationFailed(FailType.GPS_NOT_OPEN);
    }

    // For test purposes
    void setDefaultLocationSource(DefaultLocationSource defaultLocationSource1) {
        this.defaultLocationSource = defaultLocationSource1;
    }

    private DefaultLocationSource getSourceProvider() {
        if (defaultLocationSource == null) {
            defaultLocationSource = new DefaultLocationSource(getAbility(), this, this);
        }
        return defaultLocationSource;
    }


}